/// @ref gtx_io
/// @file glm/gtx/io2.inl
/// @author Jan P Springer (regnirpsj@gmail.com)

#include <iomanip>                  // std::fixed, std::setfill<>, std::setprecision, std::right, std::setw
#include <ostream>                  // std::basic_istream<>
#include <string.h>
#include "../gtc/matrix_access.hpp" // glm::col, glm::row
#include "../gtx/type_trait.hpp"    // glm::type<>

namespace glm{

    namespace detail
    {
        template <typename CTy, typename CTr, template <typename, precision> class V, typename T, precision P>
        GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>&
            load_vector_on(std::basic_istream<CTy, CTr>& is, V<T, P> & a)
        {
            typename std::basic_istream<CTy, CTr>::sentry const cerberus(is);

            char delim_left, delim_right, separator, space;

            if (cerberus)
            {
                io::format_punct<CTy> const& fmt(io::get_facet<io::format_punct<CTy> >(is));

                length_t const& components(type<V, T, P>::components);

                if (fmt.formatted)
                {
                    io::basic_state_saver<CTy> const bss(is);

                    is >> /*std::fixed >>*/ std::right >> std::setprecision(fmt.precision) >> std::setfill(fmt.space) >> delim_left;

                    for (length_t i(0); i < components; ++i)
                    {
                        is >> std::setw(fmt.width) >> a[i];
                        if (components - 1 != i)
                            is >> separator;
                    }

                    is >> delim_right;
                }
                else
                {
                    for (length_t i(0); i < components; ++i)
                    {
                        is >> a[i];

                        if (components - 1 != i)
                            is >> space;
                    }
                }
            }

            return is;
        }
    }//namespace detail

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tquat<T, P>& a)
    {
        return detail::load_vector_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tvec1<T, P>& a)
    {
        return detail::load_vector_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tvec2<T, P>& a)
    {
        return detail::load_vector_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tvec3<T, P>& a)
    {
        return detail::load_vector_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tvec4<T, P>& a)
    {
        return detail::load_vector_on(is, a);
    }

    namespace detail
    {
        template <typename CTy, typename CTr, template <typename, precision> class M, typename T, precision P>
        GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& load_matrix_on(std::basic_istream<CTy, CTr>& is, M<T, P> & a)
        {
            typename std::basic_istream<CTy, CTr>::sentry const cerberus(is);

            if (cerberus)
            {
                io::format_punct<CTy> const& fmt(io::get_facet<io::format_punct<CTy> >(is));

                length_t const& cols(type<M, T, P>::cols);
                length_t const& rows(type<M, T, P>::rows);

                char newline, delim_left, space, delim_right;
                M<T, P>::row_type r;
                M<T, P>::col_type c;

                if (fmt.formatted)
                {
                    is >> newline >> delim_left;

                    switch (fmt.order)
                    {
                    case io::column_major:
                    {
                        for (length_t i(0); i < rows; ++i)
                        {
                            if (0 != i)
                                is >> space;

                            is >> r;
                            for (length_t j = 0; j < a.length(); ++j)
                                a[j][i] = r[j];

                            if (rows - 1 != i)
                                is >> newline;
                        }
                    }
                    break;

                    case io::row_major:
                    {
                        for (length_t i(0); i < cols; ++i)
                        {
                            if (0 != i)
                                is >> space;

                            is >> c;
                            a[i] = c;

                            if (cols - 1 != i)
                                is >> newline;
                        }
                    }
                    break;
                    }

                    is >> delim_right;
                }
                else
                {
                    switch (fmt.order)
                    {
                    case io::column_major:
                    {
                        for (length_t i(0); i < cols; ++i)
                        {
                            is >> c;
                            a[i] = c;

                            if (cols - 1 != i)
                                is >> space;
                        }
                    }
                    break;

                    case io::row_major:
                    {
                        for (length_t i(0); i < rows; ++i)
                        {
                            is >> r;
                            for (length_t j = 0; j < a.length(); ++j)
                                a[j][i] = r[j];

                            if (rows - 1 != i)
                                is >> space;
                        }
                    }
                    break;
                    }
                }
            }

            return is;
        }
    }//namespace detail

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat2x2<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat2x3<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat2x4<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat3x2<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat3x3<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat3x4<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat4x2<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat4x3<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }

    template <typename CTy, typename CTr, typename T, precision P>
    GLM_FUNC_QUALIFIER std::basic_istream<CTy, CTr>& operator>>(std::basic_istream<CTy, CTr>& is, tmat4x4<T, P> & a)
    {
        return detail::load_matrix_on(is, a);
    }


}//namespace glm